// dllmain.cpp : Defines the entry point for the DLL application.
#include <GL/glew.h>
#include "stdafx.h"
#include "SimApp.h"
#include "Point3D.h"
#include "SphericalMillingTool.h"
#include "mtxlib.h"
#include "DrawElement.h"
#include "PathsDrawElement.h"
#include "BSplineGeometryManager.h"
#include <math.h>
#include <fstream>
#include <assert.h>
#include <vector>
#include <algorithm>
#include <exception>
using namespace std;

HDC _hdc;
HGLRC _hglrc;

HDC _pathPreviewHDC;
HGLRC _pathPreviewHGLRC;

SimApp * app;
GLsizei winWidth;
GLsizei winHeight;
GLfloat screenAspect;
GLuint  imgTexture;
matrix44 mProj;

Point3D cameraPos,viewUpVector,lookAt;
double fov;

vector<DrawElement*>* drawElements = nullptr;
PathsDrawElement * pathDrawElement = nullptr;



void crossProduct(float ux,float uy,float uz,float vx,float vy,float vz,float * x, float *y, float *z)
{
	*x = uy * vz - uz * vy;
	*y = -uz * vx + ux * vz;
	*z = ux * vy - uy * vx;
}

extern "C" __declspec(dllexport) void Test()
{
	int a = 0;
	a++;
}

extern "C" __declspec(dllexport) void InitGL(HDC hdc,HGLRC hglrc)
{
	_hdc = hdc;
	_hglrc = hglrc;
	if(!wglMakeCurrent(_hdc,_hglrc))
	{
		std::ofstream file;
		file.open("log.txt");
		file<<"Make current context unsuccessfull\n";
		file.flush();
		file.close();
		return;
	}

	GLenum err = glewInit();
	GLuint bid;
	glGenBuffers(1,&bid);
	glDeleteBuffers(1,&bid);
	app = NULL;
	cameraPos.x = 0.0;
	cameraPos.y = -150.0;
	cameraPos.z = 160.0;
	viewUpVector.x = 0.0;
	viewUpVector.y = 1.0;
	viewUpVector.z = 0.0;
	lookAt.x = 0.0;
	lookAt.y = 0.0;
	lookAt.z = 0.0;
	fov = 45.0;
	mProj = IdentityMatrix44();

	drawElements = new vector<DrawElement*>();
	drawElements->reserve(2);
	glEnable(GL_DEPTH_TEST);
	/*glEnable(GL_LIGHTING);
	GLfloat mat_specular [] = {1.0,1.0,1.0,1.0};
	GLfloat mat_shininess [] = {50.0};
	glShadeModel(GL_SMOOTH);
	glMaterialfv(GL_FRONT,GL_SPECULAR,mat_specular);
	glMaterialfv(GL_FRONT,GL_SHININESS,mat_shininess);

	GLfloat light_ambient[] = {1.0,0.0,0.0,1.0};
	GLfloat light_diffuse[] = {1.0,0.0,1.0,1.0};
	GLfloat light_specular[] = {1.0,0.0,1.0,1.0};
	GLfloat light_position[] = {75.0,75.0,300.0,0.0};

	glLightfv(GL_LIGHT0,GL_AMBIENT,light_ambient);
	glLightfv(GL_LIGHT0,GL_DIFFUSE,light_diffuse);
	glLightfv(GL_LIGHT0,GL_SPECULAR,light_specular);
	glLightfv(GL_LIGHT0,GL_POSITION,light_position);
	glEnable(GL_LIGHT0);
	*/
}

extern "C" __declspec(dllexport) void CreateSimulation(UINT width,UINT height,UINT depth)
{
	if(app)
		delete app;
	app = new SimApp(width,height,depth);
	DrawElement * de = dynamic_cast<DrawElement*>(app->machine);
	drawElements->push_back(de);
}

extern "C" __declspec(dllexport) void CreateMaterial(UINT width,UINT height,UINT depth)
{
	app->CreateMaterial(width,height,depth);
}

extern "C" __declspec(dllexport) void LoadPathFile(char* fName,NotifyProgressCallback progressFunc,ErrorNotifyCallback errorNotifyFunc)
{
	assert(app!=NULL);
	app->parseFile(fName,progressFunc,errorNotifyFunc);
}

extern "C" __declspec(dllexport) void ProcessStates(NotifyProgressCallback progressFuncNotify,ErrorNotifyCallback errFuncNotify)
{
	assert(app!=NULL);
	app->ProcessStates(progressFuncNotify,errFuncNotify);
}

extern "C" __declspec(dllexport) bool PerformAnimationStep(NotifyProgressCallback progressFuncNotify,ErrorNotifyCallback errFuncNotify)
{
	assert(app!=NULL);
	return app->PerformAnimationStep(progressFuncNotify,errFuncNotify);
}

extern "C" __declspec(dllexport) void RewindAnimation()
{
	assert(app!=NULL);
	app->RewindAnitmation();
}

extern "C" __declspec(dllexport) void DeleteSimulation()
{
	if(pathDrawElement!=nullptr)
		delete pathDrawElement;
	delete drawElements;
	delete app;
}

extern "C" __declspec(dllexport) void Reshape(int width,int height)
{
	winWidth = width;
	winHeight = height;
	screenAspect = (GLfloat)width/(GLfloat)height;
	GLfloat angle = 0.0F;
	glViewport(0,0,winWidth,winHeight);
	glMatrixMode(GL_PROJECTION);
	glLoadMatrixf((float*)(&mProj));
	gluPerspective(fov,screenAspect,0.1,3000.0);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(cameraPos.x,cameraPos.y,cameraPos.z,lookAt.x,lookAt.y,lookAt.z,viewUpVector.x,viewUpVector.y,viewUpVector.z);
}

extern "C" __declspec(dllexport) void ZoomIn()
{
	fov+=1.5;
	Reshape(winWidth,winHeight);
}

extern "C" __declspec(dllexport) void ZoomOut()
{
	fov-=1.5;
	Reshape(winWidth,winHeight);
}

extern "C" __declspec(dllexport) void ReshapePathPreview(int width,int height)
{
	glOrtho(0.0,app->machine->width,0.0,app->machine->height,0.1,3000.0);
}

extern "C" __declspec(dllexport) void TranslateLeft()
{
	mProj = TranslateMatrix44(-0.1f,0.0f,0.0f)*mProj;
	Reshape(winWidth,winHeight);
}

extern "C" __declspec(dllexport) void TranslateRight()
{
	mProj = TranslateMatrix44(0.1f,0.0f,0.0f)*mProj;
	Reshape(winWidth,winHeight);
}

extern "C" __declspec(dllexport) void TranslateUp()
{
	mProj = TranslateMatrix44(0.0f,0.1f,0.0f)*mProj;
	Reshape(winWidth,winHeight);
}

extern "C" __declspec(dllexport) void TranslateDown()
{
	mProj = TranslateMatrix44(0.0f,-0.1f,0.0f)*mProj;
	Reshape(winWidth,winHeight);
}

extern "C" __declspec(dllexport) double GetCameraPosX()
{
	return cameraPos.x;
}

extern "C" __declspec(dllexport) double GetCameraPosY()
{
	return cameraPos.y;
}

extern "C" __declspec(dllexport) double GetCameraPosZ()
{
	return cameraPos.z;
}

extern "C" __declspec(dllexport) void SetCameraPosX(double value)
{
	cameraPos.x = (MY_DATATYPE) value;
	Reshape(winWidth,winHeight);
}

extern "C" __declspec(dllexport) void SetCameraPosY(double value)
{
	cameraPos.y = (MY_DATATYPE) value;
	Reshape(winHeight,winWidth);
}

extern "C" __declspec(dllexport) void SetCameraPosZ(double value)
{
	cameraPos.z = (MY_DATATYPE) value;
	Reshape(winWidth,winHeight);
}

extern "C" __declspec(dllexport) double GetFOV()
{
	return fov;
}

extern "C" __declspec(dllexport) void SetFOV(double value)
{
	fov = value;
	Reshape(winWidth,winHeight);
}

extern "C" __declspec(dllexport) double GetMaterialWidth()
{
	assert(app!=NULL);
	assert(app->machine->width!=0);
	return app->machine->width;
}

extern "C" __declspec(dllexport) double GetMaterialHeight()
{
	assert(app!=NULL);
	assert(app->machine->height!=0);
	return app->machine->height;
}

extern "C" __declspec(dllexport) double GetMaterialDepth()
{
	assert(app!=NULL);
	assert(app->machine->initDepth!=0);
	return app->machine->initDepth;
}

extern "C" __declspec(dllexport) double GetPathOriginX()
{
	assert(app!=NULL);
	return app->pathCenter.x;
}

extern "C" __declspec(dllexport) double GetPathOriginY()
{
	assert(app!=NULL);
	return app->pathCenter.y;
}

extern "C" __declspec(dllexport) double GetPathOriginZ()
{
	assert(app!=NULL);
	return app->pathCenter.z;
}

extern "C" __declspec(dllexport) void SetPathOriginX(double val)
{
	assert(app!=NULL);
	app->pathCenter.x = (MY_DATATYPE) val;
}

extern "C" __declspec(dllexport) void SetPathOriginY(double val)
{
	assert(app!=NULL);
	app->pathCenter.y = (MY_DATATYPE) val;
}

extern "C" __declspec(dllexport) void SetPathOriginZ(double val)
{
	assert(app!=NULL);
	app->pathCenter.z = (MY_DATATYPE) val;
}

//-----
extern "C" __declspec(dllexport) double GetToolX()
{
	assert(app!=NULL);
	return app->GetToolX();
}

extern "C" __declspec(dllexport) double GetToolY()
{
	assert(app!=NULL);
	return app->GetToolY();
}

extern "C" __declspec(dllexport) double GetToolZ()
{
	assert(app!=NULL);
	return app->GetToolZ();
}

extern "C" __declspec(dllexport) void SetToolX(double val)
{
	assert(app!=NULL);
	app->SetToolX(val);
}

extern "C" __declspec(dllexport) void SetToolY(double val)
{
	assert(app!=NULL);
	app->SetToolY(val);
}

extern "C" __declspec(dllexport) void SetToolZ(double val)
{
	assert(app!=NULL);
	app->SetToolZ(val);
}

extern "C" __declspec(dllexport) UINT GetCurrentInstructionNumber()
{
	assert(app!=NULL);
	return app->getCurrentInstructionNumber();
}

extern "C" __declspec(dllexport) void CreateToroidalTool(double length,int diameter)
{
	assert(app!=NULL);
	app->CreateToroidalTool(length,diameter);
	app->SetToolX(0.0);
	app->SetToolY(0.0);
	app->SetToolZ(300.0);
}

extern "C" __declspec(dllexport) void CreateBallMillTool(double length,int diameter)
{
	assert(app!=NULL);
	app->CreateBallTool(length,diameter);
	app->SetToolX(0.0);
	app->SetToolY(0.0);
	app->SetToolZ(300.0);
}

extern "C" __declspec(dllexport) void SetPathVisualizationVisible(bool on)
{
	if(on)
	{
		pathDrawElement = app->GetToolPathDrawElement();
		DrawElement* de = dynamic_cast<DrawElement*>(pathDrawElement);
		drawElements->push_back(de);
	}
	else{
		delete pathDrawElement;
		pathDrawElement = nullptr;
	}
}

extern "C" __declspec(dllexport) void RotateScene(float oldX,float oldY,float newX,float newY,float maxRadius)
{
	float x,y,z;
	float oldZ,newZ;
	oldZ = sqrt(maxRadius*maxRadius-oldX*oldX-oldY*oldY);
	newZ = sqrt(maxRadius*maxRadius-newX*newX-newY*newY);
	crossProduct(oldX,oldY,oldZ,newX,newY,newZ,&x,&y,&z);
	float norm3 = sqrt(x*x+y*y+z*z);
	x/=norm3;
	y/=norm3;
	z/=norm3;
	//mProj = RotateRadMatrix44('x',x)*RotateRadMatrix44('y',y)*RotateRadMatrix44('z',z);
	mProj = InvertMatrix44(mProj)* RotateRadMatrix44('y',DegToRad(10.0f))*mProj;//*InvertMatrix44(RotateRadMatrix44('x',DegToRad(10.0f)));
	Reshape(winWidth,winHeight);
}

extern "C" __declspec(dllexport) void Render()
{
	wglMakeCurrent(_hdc,_hglrc);
	glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
	glClearColor(1.0,1.0,1.0,0.0);
	for_each(drawElements->begin(),drawElements->end(),[](DrawElement * drawElement){
		if(drawElement!=nullptr)
			drawElement->Draw();
	});
	glFlush();
	//SwapBuffers(_hdc);
}

extern "C" __declspec(dllexport) void GenerateAndSavePathFile(char * fileName)
{
	BSplineGeometryManager * bSplineManager = new BSplineGeometryManager(app->tool,app->machine);
	bSplineManager->saveToFile(fileName);
	delete bSplineManager;
}

//extern "C" __declspec(dllexport) void DisplayPartFile()
//{
//}
//
//extern "C" __declspec(dllexport) void GeneratePaths(int steps,double millingError1,double millingError2)
//{
//}

BOOL APIENTRY DllMain( HMODULE hModule,
	DWORD  ul_reason_for_call,
	LPVOID lpReserved
	)
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
	case DLL_PROCESS_DETACH:
		break;
	}
	return TRUE;
}

